﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

namespace PEUtils
{
    //日志输出器的接口
    public interface ILogger
    {
        void Log(string msg, LogColor logColor = LogColor.None);
        void Warn(string msg);
        void Error(string msg);
    }

    public static class PELog
    {
        private static ILogger logger;
        public static LogConfig config;
        private static StreamWriter LogFileWriter = null;
        private const string logLock = "PELogLock";

        public static void InitSettings(LogConfig conf = null , ILogger customLogger = null)
        {
            if (conf == null)
            {
                conf = new LogConfig();
            }
            config = conf;

            if(logger!= null)
            {
                logger = customLogger;
            }
            else
            {
                if (conf.loggerType == LoggerType.Console)
                {
                    logger = new ConsoleLogger();
                }
                else
                {
                    logger = new UnityLogger();
                }
            }
            

            if (conf.enableSave == false)
            {
                return;
            }
            if (conf.enableCover)
            {
                string path = conf.savePath + conf.saveName;
                try
                {
                    if (Directory.Exists(conf.savePath))
                    {
                        if (File.Exists(path))
                        {
                            File.Delete(path);
                        }
                    }
                    else
                    {
                        Directory.CreateDirectory(conf.savePath);
                    }
                    LogFileWriter = File.AppendText(path);
                    LogFileWriter.AutoFlush = true;
                }
                catch (Exception _)
                {
                    LogFileWriter = null;
                }
            }
            else
            {
                string prefix = DateTime.Now.ToString("yyyyMMdd@HH-mm-ss");
                string path = conf.savePath + prefix + conf.saveName;
                try
                {
                    if (Directory.Exists(conf.savePath) == false)
                    {
                        Directory.CreateDirectory(conf.savePath);
                    }
                    LogFileWriter = File.AppendText(path);
                    LogFileWriter.AutoFlush = true;
                }
                catch (Exception _)
                {
                    LogFileWriter = null;
                }
            }
        }
        public enum PrintType
        {
            Log,
            Trace,
            Warn,
            Error
        }
        public static void UniversalLog(string msg,PrintType type=PrintType.Log,LogColor color= LogColor.None)
        {
            string printType = "[L]";
            lock (logLock)
            {
                switch (type)
                {
                    case PrintType.Log:
                        logger.Log(msg,color);
                        break;
                    case PrintType.Trace:
                        printType = "[T]";
                        logger.Log(msg, color);
                        break;
                    case PrintType.Warn:
                        printType = "[W]";
                        logger.Warn(msg);
                        break;
                    case PrintType.Error:
                        printType = "[E]";
                        logger.Error(msg);
                        break;
                    default:
                        break;
                }
                if (config.enableSave)
                {
                    WriteToFile($"{printType}{msg}");
                }
            }
        }

        public static void Log(string msg, params object[] args)
        {
            if (config.enableLog == false) return;
            msg = DecorateLog(string.Format(msg, args));
            UniversalLog(msg);
        }
        public static void Log(object obj)
        {
            if (config.enableLog == false) return;
            string msg = DecorateLog(obj.ToString());
            UniversalLog(msg);
        }

        public static void ColorLog(LogColor color, string msg, params object[] args)
        {
            if (config.enableLog == false) return;
            msg = DecorateLog(string.Format(msg, args));
            UniversalLog(msg,PrintType.Log,color);
        }
        public static void ColorLog(LogColor color, object obj)
        {
            if (config.enableLog == false) return;
            string msg = DecorateLog(obj.ToString());
            UniversalLog(msg, PrintType.Log, color);
        }
        /// <summary>
        /// 支持Format的堆栈日志
        /// </summary>:
        public static void Trace(string msg, params object[] args)
        {
            if (config.enableLog == false) return;
            msg = DecorateLog(string.Format(msg, args), config.enableTrace);
            UniversalLog(msg, PrintType.Trace,LogColor.Magenta); 
        }
        public static void Trace(object obj)
        {
            if (config.enableLog == false) return;
            string msg = DecorateLog(obj.ToString(), config.enableTrace);
            UniversalLog(msg, PrintType.Trace, LogColor.Magenta);
        }
        /// <summary>
        /// 警告日志（黄色）
        /// </summary>
        public static void Warn(string msg, params object[] args)
        {
            if (config.enableLog == false) return;
            msg = DecorateLog(string.Format(msg, args));
            UniversalLog(msg, PrintType.Warn);
        }
        public static void Warn(object obj)
        {
            if (config.enableLog == false) return;
            string msg = DecorateLog(obj.ToString());
            UniversalLog(msg, PrintType.Warn);
        }
        /// <summary>
        /// 错误日志（红色，输出堆栈）
        /// </summary>
        public static void Error(string msg, params object[] args)
        {
            if (config.enableLog == false) return;
            msg = DecorateLog(string.Format(msg, args), config.enableTrace);
            UniversalLog(msg, PrintType.Error);
        }
        public static void Error(object obj)
        {
            if (config.enableLog == false) return;
            string msg = DecorateLog(obj.ToString(), config.enableTrace);
            UniversalLog(msg, PrintType.Error);
        }

        //Tool
        private static string DecorateLog(string msg, bool isTrace = false)
        {
            StringBuilder sb = new StringBuilder(config.logPrefix, 100);
            if (config.enableTime)
            {
                sb.AppendFormat(" {0}", DateTime.Now.ToString("hh:mm:ss-ffff"));
            }
            if (config.enableThreadID)
            {
                sb.AppendFormat(" {0}", GetThreadID());
            }
            sb.AppendFormat(" {0} {1}", config.logSeparate, msg);
            if (isTrace)
            {
                sb.AppendFormat("\nStackTrace:{0}", GetLogTrace());
            }
            return sb.ToString();
        }
        private static string GetThreadID()
        {
            return $" ThreadID:{Thread.CurrentThread.ManagedThreadId}";
        }
        private static string GetLogTrace()
        {
            StackTrace st = new StackTrace(3, true);//跳跃3帧
            string traceInfo = "";
            for (int i = 0; i < st.FrameCount; i++)
            {
                StackFrame sf = st.GetFrame(i);
                traceInfo += $"\n    {sf.GetFileName()}::{sf.GetMethod()} Line:{sf.GetFileLineNumber()}";
            }
            return traceInfo;
        }
        private static void WriteToFile(string msg)
        {
            if (config.enableSave && LogFileWriter != null)
            {
                try
                {
                    LogFileWriter.WriteLine(msg);
                }
                catch (Exception)
                {
                    LogFileWriter = null;
                    return;
                }
            }
        }

        //打印数组数据For Debug
        public static void PrintBytesArray(byte[] bytes, string prefix, Action<string> printer = null)
        {
            string str = prefix + "->\n";
            for (int i = 0; i < bytes.Length; i++)
            {
                if (i % 10 == 0&&i != 0)
                {
                    str += bytes[i] + "\n";
                }
                else
                    str += bytes[i] + " ";
            }
            if (printer != null)
            {
                printer(str);
            }
            else
            {
                Log(str);
            }
        }

    }
    
}
